using System.Collections;
using System.Collections.Generic;
using Main.Details;
using Main.Map;
using Unity.Mathematics;
using UnityEngine;
using ZSW.Framework;

namespace Main.AStar
{
    public class AStarController : ZSWF_Singleton<AStarController>
    {
        private GridNodes gridNodes;

        private Node starNode;

        private Node targetNode;

        private RectInt grid;

        // 当前选中Node的周围8个Node        
        private List<Node> openNodeList;

        // 所有被选中的点
        private HashSet<Node> closeNodeList;

        private bool pathFound;

        public void BuildPath(string sceneName, Vector2Int starPos, Vector2Int targetPos, Stack<MovementStep> npcMovementStack)
        {
            pathFound = false;

            if (GenerateGridNodes(sceneName, starPos, targetPos))
            {
                if (FindShortPath())
                {
                    UpdatePathOnMovementStepStack(sceneName, npcMovementStack);
                }
            }
        }

        /// <summary>
        /// 生成网格节点
        /// </summary>
        /// <param name="sceneName"></param>
        /// <param name="starPos"></param>
        /// <param name="targetPos"></param>
        /// <returns></returns>
        private bool GenerateGridNodes(string sceneName, Vector2Int starPos, Vector2Int targetPos)
        {
            var GridMapManager = ZSWF_IOCContainer.Get<GridMapManager>();

            // 根据瓦片地图范围构建网格节点
            if (GridMapManager.TryGetGridDimensions(sceneName, out grid))
            {
                gridNodes = new GridNodes(grid.width, grid.height);

                openNodeList = new List<Node>();

                closeNodeList = new HashSet<Node>();
            }
            else
            {
                return false;
            }


            gridNodes.TryGetGridNode(starPos.x - grid.position.x, starPos.y - grid.position.y, out starNode);

            gridNodes.TryGetGridNode(targetPos.x - grid.position.x, targetPos.y - grid.position.y, out targetNode);

            for (int x = 0; x < grid.width; x++)
            {
                for (int y = 0; y < grid.height; y++)
                {
                    Vector3Int tilePos = new Vector3Int(x + grid.position.x, y + grid.position.y, 0);
                    string key = sceneName + "x" + tilePos.x + "y" + tilePos.y;
                    if (GridMapManager.TryGetTileDetails(key, out TileDetails tileDetails))
                    {
                        gridNodes.TryGetGridNode(x, y, out var node);

                        node.isObstacle = tileDetails.isNPCObstacle;
                    }
                }
            }

            return true;

        }

        /// <summary>
        /// 查找最近路径
        /// </summary>
        /// <returns></returns>
        private bool FindShortPath()
        {
            // 添加起点
            openNodeList.Add(starNode);

            while (openNodeList.Count > 0)
            {

                openNodeList.Sort();

                //
                Node closeNode = openNodeList[0];

                openNodeList.RemoveAt(0);

                closeNodeList.Add(closeNode);

                if (closeNode == targetNode)
                {
                    pathFound = true;

                    break;
                }

                //TODO: 将周围8个node添加到openNodeList中(Cast赋值+障碍判断)
                EvaluateNeighborNodes(closeNode);
            }

            return pathFound;
        }

        private void EvaluateNeighborNodes(Node currentNode)
        {
            Vector2Int currentNodPos = currentNode.gridPos;

            Node validNeighborNode;

            for (int x = currentNodPos.x - 1; x <= currentNodPos.x + 1; x++)
            {
                for (int y = currentNodPos.y - 1; y <= currentNodPos.y + 1; y++)
                {
                    // 忽略自身
                    if (x == 0 && y == 0)
                    {
                        continue;
                    }
                    if (TryGetValidNeighborNode(x, y, out validNeighborNode) && !openNodeList.Contains(validNeighborNode))
                    {
                        // 计算G、HCast 值
                        validNeighborNode.gCost = currentNode.gCost + GetDistance(currentNode, validNeighborNode);
                        validNeighborNode.hCost = GetDistance(validNeighborNode, targetNode);
                        // 父节点赋值
                        validNeighborNode.parentNode = currentNode;
                        // 加入OpenList
                        openNodeList.Add(validNeighborNode);
                    }

                }
            }
        }

        /// <summary>
        /// 尝试获取有效的周围节点
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="node"></param>
        /// <returns></returns>
        private bool TryGetValidNeighborNode(int x, int y, out Node node)
        {
            if (gridNodes.TryGetGridNode(x, y, out node))
            {
                // 剔除障碍点与已访问点
                if (!node.isObstacle && !closeNodeList.Contains(node))
                {
                    return true;
                }
            }
            return false;
        }

        private int GetDistance(Node nodeA, Node nodeB)
        {
            int xDistance = Mathf.Abs(nodeA.gridPos.x - nodeB.gridPos.x);
            int yDistance = Mathf.Abs(nodeA.gridPos.y - nodeB.gridPos.y);
            if (xDistance > yDistance)
            {
                return 14 * yDistance + 10 * (xDistance - yDistance);
            }
            return 14 * xDistance + 10 * (yDistance - xDistance);
        }

        /// <summary>
        /// 更新路径每一步的坐标和场景名字
        /// </summary>
        /// <param name="sceneName"></param>
        /// <param name="npcMovementStep"></param>
        private void UpdatePathOnMovementStepStack(string sceneName, Stack<MovementStep> npcMovementStack)
        {
            Node nextNode = targetNode;

            while (nextNode != null)
            {
                MovementStep step = new MovementStep();

                step.sceneName = sceneName;

                step.gridCoordinate = nextNode.gridPos + grid.position;

                npcMovementStack.Push(step);

                nextNode = nextNode.parentNode;
            }
        }
    }
}

